package com.john.blockchain.demo.mock.task;

import com.john.common.util.DateUtil;
import com.john.common.util.RandomStrings;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author jiangguangtao 2018/4/14
 */
public class HashMineTask implements Runnable {
    private static final Logger LOGGER = LoggerFactory.getLogger(HashMineTask.class);

    private String prefixString;

    private int matchLength;

    private final MineTaskCallback callback;

    public HashMineTask(String prefixString, int matchLength, MineTaskCallback callback) {
        this.prefixString = prefixString;
        this.matchLength = matchLength;
        this.callback = callback;
    }

    @Override
    public void run() {
        startMine(callback);
    }

    private void startMine(MineTaskCallback callback) {
        MineResult mineResult = new MineResult();

        try {
            int taskRunners = Runtime.getRuntime().availableProcessors();
            CountDownLatch latch = new CountDownLatch(taskRunners);
            ArrayBlockingQueue<String> nonceQueue = new ArrayBlockingQueue<String>(50);

            LOGGER.info("待计算Hash字符串：{}", prefixString);
            HashCalcTaskContext context = new HashCalcTaskContext(latch, nonceQueue, prefixString);
            context.setMatchCodeLength(matchLength);

            ExecutorService executor = Executors.newFixedThreadPool(taskRunners + 1);

            // 首先启动随机数生成线程
            LOGGER.info("启动随机数生成线程");
            executor.submit(new HashNonceGeneratorTask(context, RandomStrings.random(4), 20));
            long beginTime = System.currentTimeMillis();
            mineResult.setBeginTime(DateUtil.getCurrentTime());

            for (int i = 0; i < taskRunners; i++) {
                LOGGER.info("启动计算线程{}", i);
                executor.submit(new HashCalcTask(context));
            }

            try {
                LOGGER.info("等待计算任务结束");
                latch.await();
            } catch (InterruptedException e) {
                LOGGER.warn("等待计算任务中断异常 {}", e);
            }
            long durations = System.currentTimeMillis() - beginTime;
            mineResult.setEndTime(DateUtil.getCurrentTime());
            mineResult.setWorkTimes(context.getCalcTimes());
            mineResult.setWorkDurations(durations);
            if (StringUtils.isNotBlank(context.getResultHash())) {
                mineResult.setResultHash(context.getResultHash());
                mineResult.setHasResult(true);
            } else {
                mineResult.setHasResult(false);
            }

            LOGGER.info("计算结束 结果：{}", mineResult);
            executor.shutdown();
        } finally {
            callback.callback(mineResult);
        }

    }
}
